package com.titanrise.crm.environment.orm;

import com.titanrise.crm.common.utils.Maybe;
import com.titanrise.crm.common.utils.ProjectUtil;
import com.titanrise.crm.environment.annotation.*;
import com.titanrise.crm.environment.ioc.SingletonPoolUtil;
import lombok.SneakyThrows;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MapperProxyUtil {
    public static Object executeSelect(Method method, Object[] args) throws SQLException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Select select = method.getAnnotation(Select.class);
        ResultType resultType = method.getAnnotation(ResultType.class);
        PreparedStatement prepared = setPreparedStatement(select.value(), method.getParameters(), args);
        ResultSet resultSet = prepared.executeQuery();

        if (method.getReturnType().equals(String.class) && resultSet.next()) {
            return resultSet.getObject(1);
        } else if (method.getReturnType().equals(int.class) && resultSet.next()) {
            Object obj = resultSet.getObject(1);
            if (obj instanceof Long)
                return ((Long) obj).intValue();
            return obj;
        } else if (method.getReturnType().equals(boolean.class) && resultSet.next()) {
            Object o = resultSet.getObject(1);
            if (o instanceof Long)
                return !o.equals(0L);
            if (o instanceof Integer)
                return !o.equals(0);
            return o;
        } else if (method.getReturnType().equals(long.class) && resultSet.next()) {
            return resultSet.getObject(1);
        }

        ResultSetMetaData metaData = resultSet.getMetaData();
        int columnCount = metaData.getColumnCount();
        List<Object> resultList = new ArrayList<>();

        while (resultSet.next()) {
            HashMap<String, Object> rowData = new HashMap<>();
            for (int i = 1; i <= columnCount; i++) {
                String columnName = metaData.getColumnLabel(i);
                Object value = resultSet.getObject(i);
                rowData.put(columnName, value);
            }

            if (method.getReturnType().equals(Map.class)) {
                resultList.add(rowData);
                continue;
            }

            Object builder = resultType.value().getMethod("builder").invoke(null);
            Class<?> clazz = builder.getClass();
            Object entity = clazz.getMethod("build").invoke(builder);
            Field[] fields = resultType.value().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                TableColumn tableColumn = field.getAnnotation(TableColumn.class);
                TableID tableID = field.getAnnotation(TableID.class);
                String colName = tableID != null
                        ? tableID.value()
                        : tableColumn != null
                        ? tableColumn.value()
                        : null;

                if (colName == null) continue;

                Object value = rowData.get(colName);

                if (field.getType().isEnum()) {
                    if (value instanceof String)
                        value = Enum.valueOf((Class<Enum>) field.getType(), (String) value);
                }

                field.set(entity, value);
            }
            resultList.add(entity);
        }
        resultSet.close();
        prepared.close();
        if (method.getReturnType().equals(List.class)) {
            return resultList;
        } else {
            return !resultList.isEmpty() ? resultList.get(0) : null;
        }
    }

    public static Integer executeUpdate(Method method, Object[] args) throws SQLException, IllegalAccessException {
        Update update = method.getAnnotation(Update.class);
        PreparedStatement prepared = setPreparedStatement(update.value(), method.getParameters(), args);
        int i = prepared.executeUpdate();
        prepared.close();
        return i;
    }

    public static Integer executeInsert(Method method, Object[] args) throws SQLException, IllegalAccessException {
        Insert insert = method.getAnnotation(Insert.class);
        PreparedStatement prepared;
        if (insert.autoGenerateUUID() && method.getParameters().length == 1 && method.getParameters()[0].isAnnotationPresent(ParamEntity.class)) {
            Connection conn = getConnection();
            prepared = conn.prepareStatement(insert.value().replaceAll("#\\{[^}]+}", "?"));
            List<String> placeholders = extractPlaceholders(insert.value());
            HashMap<String, Object> params = new HashMap<>();
            Object entity = args[0];
            ParamEntity paramEntity = method.getParameters()[0].getAnnotation(ParamEntity.class);
            for (Field field : entity.getClass().getDeclaredFields()) {
                field.setAccessible(true);
                String key = paramEntity.value() + "." + field.getName();
                if (field.isAnnotationPresent(TableID.class) && field.getType().equals(String.class)) {
                    String uuid = ProjectUtil.generateUUID();
                    field.set(entity, uuid);
                }

                if (field.getType().isEnum())
                    params.put(key, Maybe.fromNullable(field.get(entity)).map(Object::toString).orElse(null));
                else
                    params.put(key, field.get(entity));
            }

            for (int i = 0; i < placeholders.size(); i++) {
                Object paramValue = params.get(placeholders.get(i));

                // 检查 paramValue 是否为 LocalDateTime 类型
                if (paramValue instanceof LocalDateTime)
                    paramValue = Timestamp.valueOf((LocalDateTime) paramValue);

                prepared.setObject(i + 1, paramValue);
            }
        } else
            prepared = setPreparedStatement(insert.value(), method.getParameters(), args);
        int i = prepared.executeUpdate();
        prepared.close();
        return i;
    }

    public static Integer executeDelete(Method method, Object[] args) throws SQLException, IllegalAccessException {
        Delete delete = method.getAnnotation(Delete.class);
        PreparedStatement prepared = setPreparedStatement(delete.value(), method.getParameters(), args);
        int i = prepared.executeUpdate();
        prepared.close();
        return i;
    }

    public static List<String> extractPlaceholders(String input) {
        Pattern pattern = Pattern.compile("#\\{([^}]+)}");
        Matcher matcher = pattern.matcher(input);
        List<String> placeholders = new ArrayList<>();
        while (matcher.find()) {
            placeholders.add(matcher.group(1)); // 获取匹配的内容
        }
        return placeholders;
    }

    public static PreparedStatement setPreparedStatement(String sql, Parameter[] parameters, Object[] args) throws SQLException, IllegalAccessException {
        PreparedStatement prepared = getConnection().prepareStatement(sql.replaceAll("#\\{[^}]+}", "?"));
        List<String> placeholders = extractPlaceholders(sql);
        HashMap<String, Object> hashMap = new HashMap<>();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            ParamEntity paramEntity = parameters[i].getAnnotation(ParamEntity.class);
            Param param = parameters[i].getAnnotation(Param.class);

            if (paramEntity != null) {
                Object entity = args[i];
                Class<?> entityClass = entity.getClass();
                Field[] fields = entityClass.getDeclaredFields();
                String key1 = paramEntity.value();
                for (Field field : fields) {
                    field.setAccessible(true);
                    String key2 = field.getName();
                    hashMap.put(key1 + "." + key2, field.get(entity));
                }
            } else if (param != null) {
                String key = param.value();
                hashMap.put(key, args[i]);
            } else {
                String key = parameter.getName();
                hashMap.put(key, args[i]);
            }
        }

        for (int i = 0; i < placeholders.size(); i++) {
            Object obj = hashMap.get(placeholders.get(i));
            if (obj != null && obj.getClass().isEnum()) {
                try {
                    Method method = obj.getClass().getDeclaredMethod("toValue");
                    method.setAccessible(true);
                    prepared.setObject(i + 1, method.invoke(obj));
                    continue;
                } catch (NoSuchMethodException ignored) {
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
            }
            prepared.setObject(i + 1, obj);
        }
        return prepared;
    }

    @SneakyThrows
    public static Connection getConnection() {
        return SingletonPoolUtil.getBean(JdbcManager.class).getConnection();
    }
}
